#! /bin/bash

######## 备份脚本配置 ########
recovery_type=inc                  # 恢复全备还是增倍参数: # 恢复全备: full / 恢复增备: inc  
recovery_path=/data/backup/test1    # 文件恢复路径
full_backup_path=/data/backup/pxb-10.10.8.65-3306-20240521162010-full.xb  # 全备路径
inc_backup_path=/data/backup/pxb-10.10.8.65-3306-20240521162034-inc.xb  # 增备路径 # 全备时无需配置
log_path=/tmp   # 日志路径

######## XtraBackup 配置 ########
mysql_conf_path=/etc/my3306.cnf                       # mysql 配置文件路径 默认: 备份目录中的 backup-my.cnf
mysql_chown_user=mysql                                # mysql 运行用户授权 默认: mysql
xtrabackup_use_memory=4G                              # 使用内存大小 默认: 2G
xtrabackup_parallel=10                                # xtrabackup 读写线程数 默认: 4
xbstream_command="$(which xbstream 2>/dev/null)"      # xbstream 命令路径
xtrabackup_command="$(which xtrabackup 2>/dev/null)"  # xtrabackup 命令路径


######## 企业微信配置 ########
enterprise_wechat_alarm=ON           # 微信报警 开启: ON / 关闭: OFF    
enterprise_wechat_webhook_url="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=dsdsdsdsdfffddfs82" # 企业微信群机器人 webhook 地址
enterprise_wechat_tag="10.10.8.65"   # 在微信告警中添加标识信息


pxb_datatime="$(date '+%Y%m%d%H%M%S')"
log_redirection="${log_path}/1_pxb_recovery_${pxb_datatime}.log"     # 一般日志路径
pxb_log_redirection="${log_path}/2_pxb_recovery_${pxb_datatime}.log" # pxb 日志路径
# 写日志
LogWrite() {
    message_content=$1
    message_status=$2
    echo "$(date '+%Y-%m-%d %H:%M:%S')  ${message_status}  ${message_content}"  >>${log_redirection}
    echo "$(date '+%Y-%m-%d %H:%M:%S')  ${message_status}  ${message_content}"
}

# 微信告警
WeixinAlarm() {
    if [ "$enterprise_wechat_alarm" == "ON" ];then
        alarm_check(){
            # 网络是否通畅
            ping -c 1 qyapi.weixin.qq.com  >/dev/null  2>&1
            if [ $? -ne 0 ];then
                LogWrite "网络不通 无法使用微信告警功能" "error"
            fi
        }
        alarm_check

        alarm_content="$1"
        curl "${enterprise_wechat_webhook_url}"  -H 'Content-Type: application/json'  -d "{\"msgtype\": \"text\",\"text\": {\"content\": \"${enterprise_wechat_tag} ${alarm_content}\"}}" 2>/dev/null |grep 'ok' >/dev/null 2>&1
        if [ $? -ne 0 ];then
            LogWrite "微信消息发送失败 请手动验证 webhook 是否正确" "error"
            exit 1
        fi
    fi
}

# 配置默认值
ConfigDefaults() {
    full_backup_recovery_dir=$1

    if [ "${mysql_conf_path}" == '' ];then
        mysql_conf_path="$recovery_path/${pxb_datatime}_full/backup-my.cnf"
    fi
    
    if [ "$mysql_chown_user" == '' ];then
        mysql_chown_user='mysql'
    fi

    if [ "${xtrabackup_use_memory}" == '' ];then
        xtrabackup_use_memory=2G
    fi
    
    if [ "$xtrabackup_parallel" == '' ];then
        xtrabackup_parallel=4
    fi

    if [ "$log_path" == '' ];then
        log_path='/tmp'
    fi
}

# 恢复检查
RecoveryCheck() {
    check_status=0
    LogWrite "开始恢复检查···" "info"
    
    if [[ "$recovery_type" != 'full'  &&  "$recovery_type" != 'inc' ]];then
        LogWrite "recovery_type 参数错误 full / inc"  "error"
        check_status=1
    else
        if [ "$recovery_type" == 'full' ];then
            if [ ! -f "$full_backup_path" ];then
                LogWrite "$full_backup_path 全备文件不存在"  "error"
                check_status=1
            fi
        fi
    
        if [ "$recovery_type" == 'inc'  ];then
            if [ ! -f "$full_backup_path" ];then
                LogWrite "$full_backup_path 全备文件不存在"  "error"
                check_status=1
            fi
            
            if [ ! -f "$inc_backup_path" ];then
                LogWrite "$inc_backup_path 增备文件不存在"  "error"
                check_status=1
            fi 
        fi
    fi



    if [ ! -d "$log_path" ];then
        LogWrite "$log_path 日志路径不存在"
        check_status=1
    fi

    if [ ! -d "$recovery_path" ];then
        LogWrite "$recovery_path 恢复路径不存在"
        check_status=1
    fi

    if [ ! -f "$mysql_conf_path" ];then
        mysql_conf_path="$recovery_path/${pxb_datatime}_full/backup-my.cnf"
        LogWrite "mysql 配置文件未配置 已使用默认路径: $mysql_conf_path" "info"
    fi

    if [ ! -f "$xbstream_command" ];then
        LogWrite "$xbstream_command 命令不存在"
        check_status=1
    fi

    if [ ! -f "$xtrabackup_command" ];then
        LogWrite "$xtrabackup_command 命令不存在"
        check_status=1
    fi
    
    id $mysql_chown_user >/dev/null 2>&1
    if [  $? -ne 0 ];then
        LogWrite "$mysql_chown_user 授权用户不存在"
        check_status=1
    fi

    # 判断是否通过
    if [ $check_status -ne 0 ];then
        WeixinAlarm "备份检查失败"
        exit 1
    fi

    LogWrite "恢复检查已完成" "info"
    LogWrite "请检查磁盘空间是否充足 避免磁盘写满" "info"


    # 磁盘检查使用情况并微信报警
    DiskCheck(){
        # 磁盘使用率 >=90 则为 0 否则为 1
        disk_check_status=$(df -h | grep -v '/boot' | grep '^/dev/*' | awk '{gsub(/%/, ""); if ($5 >= 90) {print 0; found=1; exit}} END {if (!found) print 1}')
        if [ $disk_check_status -eq 0 ];then
        LogWrite "存在使用率大于 90% 的磁盘 请确认磁盘空间是否充足" "info"
        WeixinAlarm "存在使用率大于 90% 的磁盘 请确认磁盘空间是否充足"
        fi
    }
    DiskCheck

    LogWrite "开始恢复···" "info"

}

# 解压 .xb 文件
DecomressXbFile() {
    backup_file_path="$1"
    backup_recovery_dir="$2"

    LogWrite "解包中···" "info"
    mkdir $backup_recovery_dir

    # 解包
    cd $backup_recovery_dir && ${xbstream_command} -p ${xtrabackup_parallel} -v -x < $backup_file_path >>${pxb_log_redirection} 2>&1
    if [ $? -ne 0  ];then
        LogWrite  "解包失败"  "error"
        WeixinAlarm "解包失败"
        exit 1
    else
        LogWrite "解包成功" "info"
    fi 

    LogWrite "解压中···" "info"
    ${xtrabackup_command} --decompress  --remove-original   --parallel=${xtrabackup_parallel}   --target-dir=${backup_recovery_dir}  >>${pxb_log_redirection} 2>&1
    if [ $? -ne 0 ];then
        LogWrite "解压失败" "error"
        WeixinAlarm "解压失败"
        exit 1
    else
        decompress_status=$(tail -n 1 ${pxb_log_redirection} 2>/dev/null |grep -c  'completed OK!' 2>/dev/null)
        if [ $decompress_status -eq 1 ];then
            LogWrite "解压成功" "info"
        else
            LogWrite "解压失败" "error"
            WeixinAlarm "解压失败"
        fi
    fi
}

# 全备恢复
FullRecovery() {
    
    full_backup_recovery_dir="$recovery_path/${pxb_datatime}_full"

    # 调用默认配置
    ConfigDefaults "$full_backup_recovery_dir"

    # 解压备份文件
    DecomressXbFile "$full_backup_path" "$full_backup_recovery_dir"

    LogWrite "全量备份 prepare 中···" "info"

    # 回滚数据
    ${xtrabackup_command} --defaults-file=${mysql_conf_path}  --prepare  --use-memory=${xtrabackup_use_memory}  --parallel=${xtrabackup_parallel} --target-dir=${full_backup_recovery_dir}   >>${pxb_log_redirection} 2>&1
    if [ $? -ne 0 ];then
        LogWrite "全量备份 prepare 失败" "error"
        WeixinAlarm "全量备份 prepare 失败"
        exit 1
    else
        decompress_status=$(tail -n 1 ${pxb_log_redirection} 2>/dev/null |grep -c  'completed OK!' 2>/dev/null)
        if [ $decompress_status -eq 1 ];then
            LogWrite "全量备份 prepare 成功" "info"
        else
            LogWrite "全量备份 prepare 失败" "error"
            WeixinAlarm "全量备份 prepare 失败"
            exit 1
        fi
    fi

    # 授权用户
    chown -R $mysql_chown_user:$mysql_chown_user  $full_backup_recovery_dir  >/dev/null 2>&1
    if [ $? -ne 0 ];then
        LogWrite "恢复目录用户授权失败" "error"
    fi

    LogWrite "全量备份恢复成功!" "info"
    WeixinAlarm "全量备份恢复成功!"
}

# 增备恢复
IncRecovery() {
    full_backup_recovery_dir="$recovery_path/${pxb_datatime}_full"
    inc_backup_recovery_dir="$recovery_path/${pxb_datatime}_inc"

    # 调用默认配置
    ConfigDefaults "$full_backup_recovery_dir"

    # 解压全备文件
    DecomressXbFile "$full_backup_path" "$full_backup_recovery_dir"

    # 解压增备文件
    DecomressXbFile "$inc_backup_path" "$inc_backup_recovery_dir"

    LogWrite "全量备份 prepare 中···" "info"

    # 回滚全备
    ${xtrabackup_command} --defaults-file=${mysql_conf_path}  --prepare  --use-memory=${xtrabackup_use_memory}  --apply-log-only  --parallel=${xtrabackup_parallel} --target-dir=${full_backup_recovery_dir}   >>${pxb_log_redirection} 2>&1
    if [ $? -ne 0 ];then
        LogWrite "全量备份 prepare 失败" "error"
        WeixinAlarm "全量备份 prepare 失败"
        exit 1
    else
        decompress_status=$(tail -n 1 ${pxb_log_redirection} 2>/dev/null |grep -c  'completed OK!' 2>/dev/null)
        if [ $decompress_status -eq 1 ];then
            LogWrite "全量备份 prepare 成功" "info"
        else
            LogWrite "全量备份 prepare 失败" "error"
            WeixinAlarm "全量备份 prepare 失败"
            exit 1
        fi
    fi

    LogWrite "增量备份 prepare 中···" "info"
    
    # 回滚增倍
    ${xtrabackup_command} --defaults-file=${mysql_conf_path}  --prepare  --use-memory=${xtrabackup_use_memory}   --parallel=${xtrabackup_parallel} --target-dir=${full_backup_recovery_dir} --incremental-dir=${inc_backup_recovery_dir}   >>${pxb_log_redirection} 2>&1
    if [ $? -ne 0 ];then
        LogWrite "增量备份 prepare 失败" "error"
        WeixinAlarm "增量备份 prepare 失败"
        exit 1
    else
        decompress_status=$(tail -n 1 ${pxb_log_redirection} 2>/dev/null |grep -c  'completed OK!' 2>/dev/null)
        if [ $decompress_status -eq 1 ];then
            LogWrite "增量备份 prepare 成功" "info"
        else
            LogWrite "增量备份 prepare 失败" "error"
            WeixinAlarm "增量备份 prepare 失败"
            exit 1
        fi
    fi

    # 授权用户
    chown -R $mysql_chown_user:$mysql_chown_user  $full_backup_recovery_dir  >/dev/null 2>&1
    if [ $? -ne 0 ];then
        LogWrite "恢复目录用户授权失败" "error"
    fi

    LogWrite "增量备份恢复成功!" "info"
    WeixinAlarm "增量备份恢复成功!"
}

# 总恢复逻辑
TotalRecoveryLogic() {
    # 恢复检查
    RecoveryCheck

    # 恢复逻辑
    if [ "$recovery_type" == 'full' ];then
        FullRecovery
    elif [ "$recovery_type" == 'inc' ];then
        IncRecovery
    else
        LogWrite "$recovery_type 恢复类型错误 恢复全备: full / 恢复增备: inc " "error"
    fi
}
# 执行备份
TotalRecoveryLogic
